package ru.urfc.portal.operations;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import javax.ejb.EJB;
import javax.ejb.Stateless;

import ru.urfc.portal.dao.BalanceDao;
import ru.urfc.portal.dao.SpectrId;
import ru.urfc.portal.model.accounting.Balance;
import ru.urfc.portal.model.accounting.BalanceOperation;
import ru.urfc.portal.model.accounting.InvoiceFacture;
import ru.urfc.portal.model.accounting.Payment;
import ru.urfc.portal.model.accounting.UnifiedBalanceOperation;


@Stateless
public class BalanceOperationsAgregator {
	
	@EJB BalanceDao balanceDao;
	
	List<UnifiedBalanceOperation> agregate(List<Balance> balances, List<InvoiceFacture> factures, List<Payment> payments) {
		ArrayList<UnifiedBalanceOperation> result = new ArrayList<UnifiedBalanceOperation>();
		result.addAll(wrap(balances));
		result.addAll(wrap(factures));
		result.addAll(wrap(payments));
		Collections.sort(result);
		return enrich(result);
	}

	List<UnifiedBalanceOperation> enrich(List<UnifiedBalanceOperation> result) {
		BigDecimal balance = new BigDecimal(0);
		for (int i = 0; i < result.size(); ++i) {
			UnifiedBalanceOperation item = result.get(i);
			balance = balance.add(item.getAmountWithSign());
			item.setBalance(balance);
		}
		return result;
	}

	private Collection<UnifiedBalanceOperation> wrap(List<? extends BalanceOperation> list) {
		ArrayList<UnifiedBalanceOperation> result = new ArrayList<UnifiedBalanceOperation>();
		for (BalanceOperation item: list) {
			result.add(new UnifiedBalanceOperation(item));
		}
		return result;
	}

	private Calendar getCalendar(Date date) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		return calendar;
	}
	
	private int getYear(Date date) {
		return getCalendar(date).get(Calendar.YEAR);
	}
	
	private int getMonth(Date date) {
		return getCalendar(date).get(Calendar.MONTH) + 1;
	}

	private int getDay(Date date) {
		return getCalendar(date).get(Calendar.DAY_OF_MONTH);
	}
	
	private Date getDatePlusDay(Date date) {
		Calendar calendar = getCalendar(date);
		calendar.add(Calendar.DAY_OF_MONTH, 1);
		return calendar.getTime();
	}
	
	public Balance calculateBalanceAt(SpectrId contragentId, Date date) {
		Balance balance = balanceDao.getLastBalanceBefore(contragentId, getYear(date), getMonth(date), getDay(date));
		if (balance == null) {
			return new Balance(null, getYear(date), getMonth(date), new BigDecimal(0));
		}
		List<UnifiedBalanceOperation> operations = agregate(Arrays.asList(balance),
				balanceDao.getInvoiceFactures(contragentId, balance.getDate(), date),
				balanceDao.getPayments(contragentId, balance.getDate(), date));
		BigDecimal amount = operations.get(operations.size() - 1).getBalance();
		return new Balance(null, getYear(date), getMonth(date), getDay(date), amount);
	}

	public List<UnifiedBalanceOperation> getBalanceOperations(SpectrId contragentId, Date fromDate, Date toDate) {
		List<Balance> balanceList;
		toDate = toDate == null? new Date(): toDate;
		if (fromDate == null) {
			Date toDatePlusDay = getDatePlusDay(toDate);
			Balance balance = balanceDao.getLastBalanceBefore(contragentId,
					getYear(toDatePlusDay), getMonth(toDatePlusDay), getDay(toDatePlusDay));
			if (balance == null) {
				return new ArrayList<UnifiedBalanceOperation>();
			} else {
				balanceList = Arrays.asList(balance);
				fromDate = balance.getDate();
			}
		} else {
			balanceList = balanceDao.getBalances(contragentId, 
				getYear(fromDate), getMonth(fromDate), getDay(fromDate), 
				getYear(toDate), getMonth(toDate), getDay(toDate));
			if (balanceList.size() == 0) {
				balanceList = Arrays.asList(calculateBalanceAt(contragentId, fromDate));
			}
		}
		return agregate(balanceList,
				balanceDao.getInvoiceFactures(contragentId, fromDate, toDate),
				balanceDao.getPayments(contragentId, fromDate, toDate));
	}
	
}
